package com.opower.updater.admin.loader; import com.opower.updater.admin.Update; import org.reflections.Reflections; import org.reflections.scanners.ResourcesScanner; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.regex.Pattern; /** * Update loader that loads updates from resources in the classpath. This class needs to be extended to provide the * definition for the two template methods {@link #buildUpdateFilePattern(String)} and * {@link #extractIdFromFileName(String)}. * * @author felix.trepanier */ public abstract class ResourceUpdateLoader implements UpdateLoader { /** * Default implementation. * <p/> * It finds files that matches the pattern [numbers]-[tableName].ddl in the kiji.schema.[tableName] * package. */ public static final ResourceUpdateLoader DEFAULT = new ResourceUpdateLoader("kiji.schema.") { @Override protected String buildUpdateFilePattern(String tableName) { return "\\d+\\-" + tableName + "\\.ddl"; } @Override protected int extractIdFromFileName(String filename) { return Integer.decode(filename.split("-")[0]); } }; private final String packagePrefix; /** * Constructor for the {@link com.opower.updater.admin.loader.ResourceUpdateLoader}. The loader will only * look for update files contained in the specified package prefix followed by the table name. * * @param packagePrefix Package prefix used to build the full package where the update files are located. */ protected ResourceUpdateLoader(String packagePrefix) { this.packagePrefix = packagePrefix; } /** * {@inheritDoc} */ @Override public Update loadCreateTable(String tableName) throws IOException, TableUpdatesNotFoundException { return loadUpdates(tableName).first(); } /** * This method assumes that the DDL updates are located in the classpath * under the folder defined by {@link #packagePrefix} The updates files must respect the * pattern returned by {@link #buildUpdateFilePattern(String)}. * * @param tableName The name of the table to load updates for. * @return The table updates. * @throws java.io.IOException If an I/O error occurs * @throws TableUpdatesNotFoundException If updates for the given tables are not found. */ @Override public SortedSet<Update> loadUpdates(String tableName) throws IOException, TableUpdatesNotFoundException { Reflections reflections = new Reflections(packagePrefix + tableName, new ResourcesScanner()); Set<String> resources = reflections.getResources(Pattern.compile(buildUpdateFilePattern(tableName))); if (resources.isEmpty()) { throw new TableUpdatesNotFoundException(tableName); } SortedSet<Update> updates = new TreeSet<Update>(Update.UPDATE_COMPARATOR); for (String resource : resources) { int id = extractIdFromFileName(new File(resource).getName()); BufferedReader reader = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream(resource))); String line; StringBuilder ddl = new StringBuilder(); while ((line = reader.readLine()) != null) { ddl.append(line).append("\n"); } updates.add(new Update(id, ddl.toString())); } return updates; } /** * Construct the resource file pattern from the table name. The loader will only load files that matches the given * pattern. * * @param tableName The name of the table to load the updates for. * @return The update file pattern. */ protected abstract String buildUpdateFilePattern(String tableName); /** * Extract the update id from the file name. * * @param filename The update file name. * @return The update id. */ protected abstract int extractIdFromFileName(String filename); }